/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// s_shader.cpp -- sound shader script parsing and loading


#include "s_local.h"


#define SOUND_SHADERS_HASH_SIZE			MAX_SOUND_SHADERS / 4

typedef struct soundShaderScript_s {
	char							name[MAX_OSPATH];

	char *							buffer;
	int								size;

	struct soundShaderScript_s *	nextHash;
} soundShaderScript_t;

static soundShader_t			s_parseSoundShader;
static int						s_parseSoundFlags;

static soundShaderScript_t *	s_soundShaderScriptsHashTable[SOUND_SHADERS_HASH_SIZE];

static soundShader_t *			s_soundShadersHashTable[SOUND_SHADERS_HASH_SIZE];
static soundShader_t *			s_soundShaders[MAX_SOUND_SHADERS];
static int						s_numSoundShaders;


/*
 ==============================================================================

 SOUND SHADER PARSING

 ==============================================================================
*/


/*
 ==================
 S_ParseHighQuality
 ==================
*/
static bool S_ParseHighQuality (script_t *script, soundShader_t *soundShader){

	s_parseSoundFlags |= SF_NORESAMPLE;

	return true;
}

/*
 ==================
 S_ParsePrivate
 ==================
*/
static bool S_ParsePrivate (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_PRIVATE;

	return true;
}

/*
 ==================
 S_ParseAntiPrivate
 ==================
*/
static bool S_ParseAntiPrivate (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_ANTIPRIVATE;

	return true;
}

/*
 ==================
 S_ParseGlobal
 ==================
*/
static bool S_ParseGlobal (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_GLOBAL;

	return true;
}

/*
 ==================
 S_ParseOmnidirectional
 ==================
*/
static bool S_ParseOmnidirectional (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_OMNIDIRECTIONAL;

	return true;
}

/*
 ==================
 S_ParseLooping
 ==================
*/
static bool S_ParseLooping (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_LOOPING;

	return true;
}

/*
 ==================
 S_ParsePlayOnce
 ==================
*/
static bool S_ParsePlayOnce (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_PLAYONCE;

	return true;
}

/*
 ==================
 S_ParseNoDups
 ==================
*/
static bool S_ParseNoDups (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_NODUPS;

	return true;
}

/*
 ==================
 S_ParseNoStop
 ==================
*/
static bool S_ParseNoStop (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_NOSTOP;

	return true;
}

/*
 ==================
 S_ParseNoOffset
 ==================
*/
static bool S_ParseNoOffset (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_NOOFFSET;

	return true;
}

/*
 ==================
 S_ParseNoOcclusion
 ==================
*/
static bool S_ParseNoOcclusion (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_NOOCCLUSION;

	return true;
}

/*
 ==================
 S_ParseNoReverb
 ==================
*/
static bool S_ParseNoReverb (script_t *script, soundShader_t *soundShader){

	soundShader->flags |= SSF_NOREVERB;

	return true;
}

/*
 ==================
 S_ParsePriority
 ==================
*/
static bool S_ParsePriority (script_t *script, soundShader_t *soundShader){

	token_t	token;

	if (!PS_ReadToken(script, &token)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'priority' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (!Str_ICompare(token.string, "lowest"))
		soundShader->priority = SP_LOWEST;
	else if (!Str_ICompare(token.string, "low"))
		soundShader->priority = SP_LOW;
	else if (!Str_ICompare(token.string, "normal"))
		soundShader->priority = SP_NORMAL;
	else if (!Str_ICompare(token.string, "high"))
		soundShader->priority = SP_HIGH;
	else if (!Str_ICompare(token.string, "highest"))
		soundShader->priority = SP_HIGHEST;
	else {
		soundShader->priority = (soundPriority_t)token.signedIntValue;

		if (soundShader->priority <= SP_BAD || soundShader->priority > SP_HIGHEST){
			Com_Printf(S_COLOR_YELLOW "WARNING: unknown 'priority' parameter '%s' in sound shader '%s'\n", token.string, soundShader->name);
			return false;
		}
	}

	return true;
}

/*
 ==================
 S_ParseShakes
 ==================
*/
static bool S_ParseShakes (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->shakes)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'shakes' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->shakes < 0.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'shakes' in sound shader '%s'\n", soundShader->shakes, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseVolume
 ==================
*/
static bool S_ParseVolume (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->volume)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'volume' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->volume < 0.0f || soundShader->volume > 1.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'volume' in sound shader '%s'\n", soundShader->volume, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParsePitch
 ==================
*/
static bool S_ParsePitch (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->pitch)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'pitch' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->pitch < 0.5f || soundShader->pitch > 2.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'pitch' in sound shader '%s'\n", soundShader->pitch, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseMinDistance
 ==================
*/
static bool S_ParseMinDistance (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->minDistance)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'minDistance' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->minDistance < 0.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'minDistance' in sound shader '%s'\n", soundShader->minDistance, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseMaxDistance
 ==================
*/
static bool S_ParseMaxDistance (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->maxDistance)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'maxDistance' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->maxDistance < 0.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'maxDistance' in sound shader '%s'\n", soundShader->maxDistance, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseRolloffFactor
 ==================
*/
static bool S_ParseRolloffFactor (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->rolloffFactor)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'rolloffFactor' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->rolloffFactor < 0.0f || soundShader->rolloffFactor > 10.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'rolloffFactor' in sound shader '%s'\n", soundShader->rolloffFactor, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseDopplerFactor
 ==================
*/
static bool S_ParseDopplerFactor (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadFloat(script, &soundShader->dopplerFactor)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'dopplerFactor' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->dopplerFactor < 0.0f || soundShader->dopplerFactor > 10.0f){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %g for 'dopplerFactor' in sound shader '%s'\n", soundShader->dopplerFactor, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseMinSamples
 ==================
*/
static bool S_ParseMinSamples (script_t *script, soundShader_t *soundShader){

	if (!PS_ReadSignedInt(script, &soundShader->minSamples)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'minSamples' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (soundShader->minSamples < 1 || soundShader->minSamples > MAX_SOUNDS_PER_SHADER){
		Com_Printf(S_COLOR_YELLOW "WARNING: invalid value of %i for 'minSamples' in sound shader '%s'\n", soundShader->minSamples, soundShader->name);
		return false;
	}

	return true;
}

/*
 ==================
 S_ParseSample
 ==================
*/
static bool S_ParseSample (script_t *script, soundShader_t *soundShader){

	token_t	token;

	if (soundShader->numSounds == MAX_SOUNDS_PER_SHADER){
		Com_Printf(S_COLOR_YELLOW "WARNING: MAX_SOUNDS_PER_SHADER hit in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (!PS_ReadToken(script, &token)){
		Com_Printf(S_COLOR_YELLOW "WARNING: missing parameters for 'sample' in sound shader '%s'\n", soundShader->name);
		return false;
	}

	if (s_maxSoundsPerShader->integerValue > 0){
		if (soundShader->numSounds >= s_maxSoundsPerShader->integerValue){
			if (!soundShader->minSamples || soundShader->numSounds == soundShader->minSamples)
				return true;
		}
	}

	soundShader->sounds[soundShader->numSounds] = S_FindSound(token.string, s_parseSoundFlags);
	if (!soundShader->sounds[soundShader->numSounds]){
		Com_Printf(S_COLOR_YELLOW "WARNING: couldn't find sound '%s' for sound shader '%s'\n", token.string, soundShader->name);
		return false;
	}

	soundShader->numSounds++;

	return true;
}


// ============================================================================

typedef struct {
	const char *				name;
	bool						(*parseCmd)(script_t *, soundShader_t *);
} soundShaderCmd_t;

static soundShaderCmd_t			s_soundShaderCmds[] = {
	{"highQuality",					S_ParseHighQuality},
	{"private",						S_ParsePrivate},
	{"antiPrivate",					S_ParseAntiPrivate},
	{"global",						S_ParseGlobal},
	{"omnidirectional",				S_ParseOmnidirectional},
	{"looping",						S_ParseLooping},
	{"playOnce",					S_ParsePlayOnce},
	{"noDups",						S_ParseNoDups},
	{"noStop",						S_ParseNoStop},
	{"noOffset",					S_ParseNoOffset},
	{"noOcclusion",					S_ParseNoOcclusion},
	{"noReverb",					S_ParseNoReverb},
	{"priority",					S_ParsePriority},
	{"shakes",						S_ParseShakes},
	{"volume",						S_ParseVolume},
	{"pitch",						S_ParsePitch},
	{"minDistance",					S_ParseMinDistance},
	{"maxDistance",					S_ParseMaxDistance},
	{"rolloffFactor",				S_ParseRolloffFactor},
	{"dopplerFactor",				S_ParseDopplerFactor},
	{"minSamples",					S_ParseMinSamples},
	{"sample",						S_ParseSample},
	{NULL,							NULL}
};


/*
 ==================
 S_ParseCommand
 ==================
*/
static bool S_ParseCommand (script_t *script, const char *command, soundShader_t *soundShader){

	soundShaderCmd_t	*cmd;

	for (cmd = s_soundShaderCmds; cmd->name; cmd++){
		if (!Str_ICompare(cmd->name, command))
			return cmd->parseCmd(script, soundShader);
	}

	Com_Printf(S_COLOR_YELLOW "WARNING: unknown command '%s' in sound shader '%s'\n", command, soundShader->name);

	return false;
}

/*
 ==================
 S_ParseSoundShader
 ==================
*/
static bool S_ParseSoundShader (script_t *script, soundShader_t *soundShader){

	token_t	token;

	// Parse the sound shader
	if (!PS_ExpectTokenString(script, &token, "{")){
		Com_Printf(S_COLOR_YELLOW "WARNING: expected '{', found '%s' instead in sound shader '%s'\n", token.string, soundShader->name);
		return false;
	}

	while (1){
		if (!PS_ReadToken(script, &token)){
			Com_Printf(S_COLOR_YELLOW "WARNING: no concluding '}' in sound shader '%s'\n", soundShader->name);
			return false;	// End of data
		}

		if (!Str_Compare(token.string, "}"))
			break;			// End of sound shader

		// Parse the command
		if (!S_ParseCommand(script, token.string, soundShader))
			return false;
	}

	return true;
}

/*
 ==================
 S_ParseSoundShaderFile
 ==================
*/
static void S_ParseSoundShaderFile (script_t *script){

	soundShaderScript_t	*soundShaderScript;
	token_t				token;
	const char			*buffer;
	int					size;
	uint				hashKey;

	while (1){
		// Parse the name
		if (!PS_ReadToken(script, &token))
			break;		// End of data

		// Parse the script
		if (PS_EndOfScript(script))
			break;		// End of data

		buffer = script->text;

		PS_SkipBracedSection(script, 0);

		size = script->text - buffer;

		// Store the sound shader script
		soundShaderScript = (soundShaderScript_t *)Mem_Alloc(sizeof(soundShaderScript_t), TAG_SOUND);

		Str_Copy(soundShaderScript->name, token.string, sizeof(soundShaderScript->name));

		soundShaderScript->buffer = (char *)Mem_Alloc(size + 1, TAG_SOUND);
		soundShaderScript->size = size;

		memcpy(soundShaderScript->buffer, buffer, size);
		soundShaderScript->buffer[size] = 0;

		// Add to hash table
		hashKey = Str_HashKey(soundShaderScript->name, SOUND_SHADERS_HASH_SIZE, false);

		soundShaderScript->nextHash = s_soundShaderScriptsHashTable[hashKey];
		s_soundShaderScriptsHashTable[hashKey] = soundShaderScript;
	}
}


/*
 ==============================================================================

 SOUND SHADER LOADING

 ==============================================================================
*/


/*
 ==================
 S_NewSoundShader
 ==================
*/
static soundShader_t *S_NewSoundShader (void){

	soundShader_t	*soundShader;

	// Clear the sound shader
	soundShader = &s_parseSoundShader;
	memset(soundShader, 0, sizeof(soundShader_t));

	// Clear sound flags
	s_parseSoundFlags = 0;

	return soundShader;
}

/*
 ==================
 S_CreateDefaultSoundShader
 ==================
*/
static soundShader_t *S_CreateDefaultSoundShader (const char *name){

	soundShader_t	*soundShader;

	// Create a new sound shader
	soundShader = S_NewSoundShader();

	// Fill it in
	Str_Copy(soundShader->name, name, sizeof(soundShader->name));
	soundShader->flags = SSF_DEFAULTED;
	soundShader->volume = 1.0f;
	soundShader->pitch = 1.0f;
	soundShader->minDistance = 100.0f;
	soundShader->maxDistance = 1500.0f;
	soundShader->rolloffFactor = 1.0f;
	soundShader->dopplerFactor = 0.0f;

	soundShader->sounds[soundShader->numSounds++] = snd.defaultSound;

	return soundShader;
}

/*
 ==================
 S_CreateSoundShader
 ==================
*/
static soundShader_t *S_CreateSoundShader (const char *name, soundShaderScript_t *soundShaderScript){

	soundShader_t	*soundShader;
	script_t		*script;

	// Create a new sound shader
	soundShader = S_NewSoundShader();

	// Fill it in
	Str_Copy(soundShader->name, name, sizeof(soundShader->name));
	soundShader->flags = 0;
	soundShader->volume = 1.0f;
	soundShader->pitch = 1.0f;
	soundShader->minDistance = 100.0f;
	soundShader->maxDistance = 1500.0f;
	soundShader->rolloffFactor = 1.0f;
	soundShader->dopplerFactor = 0.0f;

	// If we have a script, create an external sound shader
	if (soundShaderScript){
		soundShader->flags |= SSF_EXTERNAL;

		// Load the script text
		script = PS_LoadScriptMemory(soundShaderScript->name, soundShaderScript->buffer, soundShaderScript->size);
		if (!script)
			return S_CreateDefaultSoundShader(name);

		PS_SetScriptFlags(script, SF_NOWARNINGS | SF_NOERRORS | SF_ALLOWPATHNAMES);

		// Parse it
		if (!S_ParseSoundShader(script, soundShader)){
			PS_FreeScript(script);

			return S_CreateDefaultSoundShader(name);
		}

		PS_FreeScript(script);

		return soundShader;
	}

	// Otherwise create an internal sound shader
	soundShader->sounds[soundShader->numSounds] = S_FindSound(Str_VarArgs("%s.wav", soundShader->name), 0);
	if (!soundShader->sounds[soundShader->numSounds]){
		Com_Printf(S_COLOR_YELLOW "WARNING: couldn't find sound for sound shader '%s', using default...\n", soundShader->name);
		soundShader->sounds[soundShader->numSounds] = snd.defaultSound;
	}

	soundShader->numSounds++;

	return soundShader;
}

/*
 ==================
 S_FinishSoundShader
 ==================
*/
static void S_FinishSoundShader (soundShader_t *soundShader){

	// 'global' ignores 'omnidirectional'
	if (soundShader->flags & SSF_GLOBAL)
		soundShader->flags &= ~SSF_OMNIDIRECTIONAL;

	// Set 'priority' if unset
	if (soundShader->priority == SP_BAD)
		soundShader->priority = SP_NORMAL;

	// Make sure 'maxDistance' is never less than 'minDistance'
	if (soundShader->maxDistance < soundShader->minDistance)
		soundShader->maxDistance = soundShader->minDistance;

	// Make sure it has a sound
	if (!soundShader->numSounds){
		Com_Printf(S_COLOR_YELLOW "WARNING: sound shader '%s' has no sound!\n", soundShader->name);

		soundShader->sounds[soundShader->numSounds++] = snd.defaultSound;
	}
}

/*
 ==================
 S_LoadSoundShader
 ==================
*/
static soundShader_t *S_LoadSoundShader (soundShader_t *newSoundShader){

	soundShader_t	*soundShader;
	uint			hashKey;

	if (s_numSoundShaders == MAX_SOUND_SHADERS)
		Com_Error(false, "S_LoadSoundShader: MAX_SOUND_SHADERS hit");

	s_soundShaders[s_numSoundShaders++] = soundShader = (soundShader_t *)Mem_Alloc(sizeof(soundShader_t), TAG_SOUND);

	// Copy the sound shader
	memcpy(soundShader, newSoundShader, sizeof(soundShader_t));

	// Make sure all the parameters are valid
	S_FinishSoundShader(soundShader);

	// Add to hash table
	hashKey = Str_HashKey(soundShader->name, SOUND_SHADERS_HASH_SIZE, false);

	soundShader->nextHash = s_soundShadersHashTable[hashKey];
	s_soundShadersHashTable[hashKey] = soundShader;

	return soundShader;
}

/*
 ==================
 S_FindSoundShader
 ==================
*/
soundShader_t *S_FindSoundShader (const char *name){

	soundShader_t		*soundShader;
	soundShaderScript_t	*soundShaderScript;
	uint				hashKey;

	// Performance evaluation option
	if (s_singleSoundShader->integerValue)
		return snd.defaultSoundShader;

	// See if already loaded
	hashKey = Str_HashKey(name, SOUND_SHADERS_HASH_SIZE, false);

	for (soundShader = s_soundShadersHashTable[hashKey]; soundShader; soundShader = soundShader->nextHash){
		if (!Str_ICompare(soundShader->name, name))
			return soundShader;
	}

	// See if there's a script for this sound shader
	for (soundShaderScript = s_soundShaderScriptsHashTable[hashKey]; soundShaderScript; soundShaderScript = soundShaderScript->nextHash){
		if (!Str_ICompare(soundShaderScript->name, name))
			break;
	}

	// Create the sound shader
	soundShader = S_CreateSoundShader(name, soundShaderScript);

	// Load it in
	return S_LoadSoundShader(soundShader);
}

/*
 ==================
 S_RegisterSoundShader
 ==================
*/
soundShader_t *S_RegisterSoundShader (const char *name){

	return S_FindSoundShader(name);
}


// ============================================================================


/*
 ==================
 S_EnumSoundShaderScripts

 This is for use by integrated editors
 ==================
*/
void S_EnumSoundShaderScripts (void (*callback)(const char *)){

	soundShaderScript_t	*soundShaderScript;
	int					i;

	for (i = 0; i < SOUND_SHADERS_HASH_SIZE; i++){
		soundShaderScript = s_soundShaderScriptsHashTable[i];

		while (soundShaderScript){
			callback(soundShaderScript->name);

			soundShaderScript = soundShaderScript->nextHash;
		}
	}
}

/*
 ==================
 S_ListSoundShaders_f
 ==================
*/
static void S_ListSoundShaders_f (void){

	soundShader_t	*soundShader;
	int				i;

	Com_Printf("\n");
	Com_Printf("      snds pri src -name--------\n");

	for (i = 0; i < s_numSoundShaders; i++){
		soundShader = s_soundShaders[i];

		Com_Printf("%4i: ", i);

		Com_Printf("%4i ", soundShader->numSounds);

		Com_Printf("%3i ", soundShader->priority);

		if (soundShader->flags & SSF_EXTERNAL)
			Com_Printf(" E  ");
		else
			Com_Printf(" I  ");

		Com_Printf("%s%s\n", soundShader->name, (soundShader->flags & SSF_DEFAULTED) ? " (DEFAULTED)" : "");
	}

	Com_Printf("--------------------------------\n");
	Com_Printf("%i total sound shaders\n", s_numSoundShaders);
	Com_Printf("\n");
}

/*
 ==================
 S_CreateBuiltInSoundShaders
 ==================
*/
static void S_CreateBuiltInSoundShaders (void){

	soundShader_t	*soundShader;

	// Default sound shader
	soundShader = S_NewSoundShader();

	Str_Copy(soundShader->name, "_default", sizeof(soundShader->name));
	soundShader->flags = SSF_NOOCCLUSION | SSF_NOREVERB;
	soundShader->volume = 1.0f;
	soundShader->pitch = 1.0f;
	soundShader->minDistance = 100.0f;
	soundShader->maxDistance = 1500.0f;
	soundShader->rolloffFactor = 1.0f;
	soundShader->dopplerFactor = 0.0f;
	soundShader->sounds[soundShader->numSounds++] = snd.defaultSound;

	snd.defaultSoundShader = S_LoadSoundShader(soundShader);
}

/*
 ==================
 S_InitSoundShaders
 ==================
*/
void S_InitSoundShaders (void){

	script_t	*script;
	char		name[MAX_OSPATH];
	const char	**fileList;
	int			numFiles;
	int			i;

	Com_Printf("Initializing Sound Shaders\n");

	// Register our commands
	Cmd_AddCommand("listSoundShaders", S_ListSoundShaders_f, "Lists loaded sound shaders");

	// Load and parse .sndshd files
	fileList = FS_ListFiles("sounds", ".sndshd", true, &numFiles);

	for (i = 0; i < numFiles; i++){
		// Load the script file
		Str_SPrintf(name, sizeof(name), "sounds/%s", fileList[i]);
		Com_Printf("...loading '%s'", name);

		script = PS_LoadScriptFile(name);
		if (!script){
			Com_Printf(": failed\n");
			continue;
		}
		Com_Printf("\n");

		PS_SetScriptFlags(script, SF_NOWARNINGS | SF_NOERRORS | SF_ALLOWPATHNAMES);

		// Parse it
		S_ParseSoundShaderFile(script);

		PS_FreeScript(script);
	}

	FS_FreeFileList(fileList);

	// Create built-in sound shaders
	S_CreateBuiltInSoundShaders();
}

/*
 ==================
 S_ShutdownSoundShaders
 ==================
*/
void S_ShutdownSoundShaders (void){

	// Unregister our commands
	Cmd_RemoveCommand("listSoundShaders");

	// Clear sound shader script and sound shader lists
	memset(s_soundShaderScriptsHashTable, 0, sizeof(s_soundShaderScriptsHashTable));

	memset(s_soundShadersHashTable, 0, sizeof(s_soundShadersHashTable));
	memset(s_soundShaders, 0, sizeof(s_soundShaders));

	s_numSoundShaders = 0;
}
